package com.ruoyi.system.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

import cn.hutool.core.date.DateUtil;
import com.ruoyi.common.enums.LiveStatus;
import com.ruoyi.common.enums.WithdrawStatus;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.CodeUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.GoogleAuthenticatorUtils;
import com.ruoyi.common.utils.exception.AjaxException;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.mapper.*;
import com.ruoyi.system.service.ISysWithdrawRuleService;
import com.ruoyi.system.vo.SysWithdrawRuleRes;
import com.ruoyi.system.vo.WithdrawStatisticsRes;
import com.ruoyi.system.vo.req.SysWithdrawReq;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.ruoyi.system.service.ISysWithdrawService;
import com.ruoyi.common.core.text.Convert;
import org.springframework.transaction.annotation.Transactional;

/**
 * 代付订单Service业务层处理
 *
 * @author Ericcc
 * @date 2021-03-16
 */
@Service
public class SysWithdrawServiceImpl implements ISysWithdrawService
{
    @Autowired
    private SysWithdrawMapper sysWithdrawMapper;

    @Autowired
    private SysUserGoogleMapper sysUserGoogleMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysUserChannelOutMapper sysUserChannelOutMapper;

    @Autowired
    private SysChannelOutMapper sysChannelOutMapper;

    @Autowired
    private SysWithdrawRuleMapper sysWithdrawRuleMapper;

    @Autowired
    private SysUserAccountMapper sysUserAccountMapper;

    @Autowired
    private ISysWithdrawService sysWithdrawService;

    /**
     * 查询代付订单
     *
     * @param withdrawId 代付订单ID
     * @return 代付订单
     */
    @Override
    public SysWithdraw selectSysWithdrawById(Long withdrawId)
    {
        return sysWithdrawMapper.selectSysWithdrawById(withdrawId);
    }

    /**
     * 查询代付订单列表
     *
     * @param sysWithdraw 代付订单
     * @return 代付订单
     */
    @Override
    public List<SysWithdraw> selectSysWithdrawList(SysWithdraw sysWithdraw)
    {
        return sysWithdrawMapper.selectSysWithdrawList(sysWithdraw);
    }

    /**
     * 新增代付订单
     *
     * @param sysWithdrawReq 代付订单
     * @return 结果
     */
    @Override
    @Transactional
    public int insertSysWithdraw(SysWithdrawReq sysWithdrawReq)
    {
        //检查用户状态
        SysUser sysUser = sysUserMapper.selectUserByUserCode(sysWithdrawReq.getUserCode());
        if (sysUser==null){
            throw new AjaxException("user is invalid");
        }
        //检查父级状态
        if (sysUser.getParentId()!=null){
            SysUser agent = sysUserMapper.selectUserById(sysUser.getParentId());
            if (agent==null){
                throw new AjaxException("agent is invalid");
            }
            if (agent.getParentId()!=null){
                SysUser highAgent = sysUserMapper.selectUserById(agent.getParentId());
                if (highAgent==null){
                    throw new AjaxException("highAgent is invalid");
                }
            }
        }
        //检查用户通道状态
        SysUserChannelOut sysUserChannelOut = sysUserChannelOutMapper.selectSysUserChannelOutById(sysUser.getUserId());
        if (sysUserChannelOut==null){
            throw new AjaxException("user's channel error");
        }
        //检查通道状态
        SysChannelOut sysChannelOut = sysChannelOutMapper.selectSysChannelOutById(sysUserChannelOut.getChannelOutId());
        if (sysChannelOut==null){
            throw new AjaxException("channel error");
        }
        if (sysChannelOut.getStatus()== LiveStatus.DISABLE.getCode()){
            throw new AjaxException("channel is close");
        }
        //查询谷歌验证码
        SysUserGoogle sysUserGoogle = sysUserGoogleMapper.selectSysUserGoogleByUserId(sysWithdrawReq.getUserId());
        if (sysUserGoogle == null) {
            throw new BusinessException("谷歌秘钥不存在");
        }
        if (!GoogleAuthenticatorUtils.authcode(sysWithdrawReq.getGooglePassword(), sysUserGoogle.getGoogleKey())) {
            throw new BusinessException("谷歌秘钥错误 请重试");
        }

        //判断代付规则
        List<SysWithdrawRuleRes> sysWithdrawRules = sysWithdrawRuleMapper.selectSysWithdrawRuleResList(new SysWithdrawRule());
        if (sysWithdrawRules == null){
            throw new BusinessException("system error:withdraw rule");
        }
        SysWithdrawRuleRes sysWithdrawRuleRes = sysWithdrawRules.get(0);
        checkWithdrawRule(sysWithdrawRuleRes,sysWithdrawReq);

        //判断订单号是否重复(数据库层面)
        int count = sysWithdrawMapper.checkByOrderChildCode(sysWithdrawReq.getWithdrawChildCode());
        if (count > 0) {
            throw new AjaxException("Duplicate withdraw number");
        }
        //判断订单号是否重复(缓存 避免高并发下数据层面筛选不了)
        String key = "checkPaid_" + sysWithdrawReq.getWithdrawChildCode();
        boolean flag = stringRedisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), sysWithdrawReq.getWithdrawChildCode().getBytes());
        if (!flag) {
            throw new AjaxException("Duplicate withdraw number");
        }
        stringRedisTemplate.expire(key, 180, java.util.concurrent.TimeUnit.SECONDS);

        SysWithdraw sysWithdraw = new SysWithdraw();
        BeanUtils.copyProperties(sysWithdrawReq,sysWithdraw);
        //计算费率(写死从商户余额扣 后期有别的方法再增设)
        BigDecimal rate = sysUserChannelOut.getUserChannelRate1();
        sysWithdraw.setWithdrawCharge(sysWithdraw.getWithdrawAmount().multiply(rate).add(sysUserChannelOut.getUserChannelFee()));
        sysWithdraw.setWithdrawLastAmount(sysWithdraw.getWithdrawAmount().add(sysWithdraw.getWithdrawCharge()));
        //账户中是否有足够的金额代付
        SysUserAccount sysUserAccount = sysUserAccountMapper.selectSysUserAccountById(sysUser.getUserId());
        //获取代付中的金额
        BigDecimal procceed = sysWithdrawService.selectSysProcceedWithdraw(sysUser.getUserCode());
        if (procceed==null){
            procceed = BigDecimal.ZERO;
        }
        if(sysWithdraw.getWithdrawLastAmount().add(procceed).compareTo(sysUserAccount.getBalance())>0){
            throw new AjaxException("余额不足");
        }
        //通道信息
        sysWithdraw.setChannelOutId(sysUserChannelOut.getChannelOutId());
        sysWithdraw.setChannelOutName(sysChannelOut.getChannelOutName());
        //订单号
        sysWithdraw.setWithdrawCode(String.valueOf(CodeUtils.getCode()));
        //代付规则
        sysWithdraw.setWithdrawRuleType(sysWithdrawRuleRes.getWithdrawRuleType());
        sysWithdraw.setDeductType(sysWithdrawRuleRes.getDeductType());
        //代付状态
        sysWithdraw.setStatus(WithdrawStatus.DISABLE.getCode());

        sysWithdraw.setCreateTime(DateUtils.getNowDate());
        try {
            sysWithdrawMapper.insertSysWithdraw(sysWithdraw);
        }catch (Exception e){
            throw new AjaxException("system error,please try again");
        }finally {
            stringRedisTemplate.delete(key);
        }

//        boolean reqKey = paidReq(sysWithdraw);
        return 1;
    }

    /**
     * 判断代付规则
     * @param sysWithdrawRuleRes
     * @param sysWithdrawReq
     */
    private void checkWithdrawRule(SysWithdrawRuleRes sysWithdrawRuleRes, SysWithdrawReq sysWithdrawReq) {
        if (sysWithdrawRuleRes.getWithdrawRuleStatus()==LiveStatus.DISABLE.getCode()){
            throw new BusinessException("withdraw forbbiden");
        }
        //是否大于风控最小金额
        if (sysWithdrawReq.getWithdrawAmount().compareTo(sysWithdrawRuleRes.getMinBalance()) < 0) {
            throw new BusinessException("pay is a minimum of " + sysWithdrawRuleRes.getMinBalance().setScale(0));
        }
        //是否小于风控最大金额
        if (sysWithdrawReq.getWithdrawAmount().compareTo(sysWithdrawRuleRes.getMaxBalance()) > 0) {
            throw new BusinessException("the largest balance is " + sysWithdrawRuleRes.getMaxBalance().setScale(0));
        }
        //是否在风控允许交易的时间范围内
        Date startTime = DateUtil.parse(sysWithdrawRuleRes.getStartTime());
        Date endTime = DateUtil.parse(sysWithdrawRuleRes.getEndTime());
        Date dateTime = new Date();
        if (startTime.getTime()!=endTime.getTime()) {
            if (dateTime.getTime() < startTime.getTime()) {
                throw new BusinessException("pay is start at about " + startTime);
            }
            if (dateTime.getTime() > endTime.getTime()) {
                throw new BusinessException("pay is end at about " + endTime);
            }
        }
        //是否超过风控总金额
        BigDecimal todayBalance = sysWithdrawMapper.getTimeRangeBalance(DateUtils.getNowDay(1));
        if (todayBalance==null){
            todayBalance = BigDecimal.ZERO;
        }
        if (todayBalance.add(sysWithdrawReq.getWithdrawAmount()).compareTo(sysWithdrawRuleRes.getTodayBalance())>0){
            throw new BusinessException("That's more than today's maximum balance");
        }
    }

    /**
     * 修改代付订单
     *
     * @param sysWithdraw 代付订单
     * @return 结果
     */
    @Override
    public int updateSysWithdraw(SysWithdraw sysWithdraw)
    {
        return sysWithdrawMapper.updateSysWithdraw(sysWithdraw);
    }

    /**
     * 删除代付订单对象
     *
     * @param ids 需要删除的数据ID
     * @return 结果
     */
    @Override
    public int deleteSysWithdrawByIds(String ids)
    {
        return sysWithdrawMapper.deleteSysWithdrawByIds(Convert.toStrArray(ids));
    }

    /**
     * 删除代付订单信息
     *
     * @param withdrawId 代付订单ID
     * @return 结果
     */
    @Override
    public int deleteSysWithdrawById(Long withdrawId)
    {
        return sysWithdrawMapper.deleteSysWithdrawById(withdrawId);
    }

    /**
     * 查询代付统计
     * @param sysWithdraw
     * @return
     */
    @Override
    public WithdrawStatisticsRes selectSysWithdrawStatistics(SysWithdraw sysWithdraw) {
        return sysWithdrawMapper.selectSysWithdrawStatistics(sysWithdraw);
    }

    /**
     * 查询单位时间内成功代付金额
     * @param userCode
     * @param beginTime
     * @return
     */
    @Override
    public BigDecimal selectSysWithdrawAmount(Integer userCode,Date beginTime) {
        return sysWithdrawMapper.selectSysWithdrawAmount(userCode,beginTime);
    }

    /**
     * 查询处理中的代付金额
     * @param userCode
     * @return
     */
    @Override
    public BigDecimal selectSysProcceedWithdraw(Integer userCode) {
        return sysWithdrawMapper.selectSysProcceedWithdraw(userCode);
    }

    /**
     * 获取代理底下商户代付列表
     * @param sysWithdraw
     * @return
     */
    @Override
    public List<SysWithdraw> selectSysAgentWithdrawList(SysWithdraw sysWithdraw) {
        return sysWithdrawMapper.selectSysAgentWithdrawList(sysWithdraw);
    }

    /**
     * 获取代理底下商户代付统计
     * @param sysWithdraw
     * @return
     */
    @Override
    public WithdrawStatisticsRes selectSysAgentWithdrawStatistics(SysWithdraw sysWithdraw) {
        return sysWithdrawMapper.selectSysAgentWithdrawStatistics(sysWithdraw);
    }

    /**
     * 查询单位时间内代理底下商户成功代付金额
     * @param userCode
     * @param beginTime
     * @return
     */
    @Override
    public BigDecimal selectAgentSysWithdrawAmount(Integer userCode, Date beginTime,long parentId) {
        return sysWithdrawMapper.selectAgentSysWithdrawAmount(userCode,beginTime,parentId);
    }

    /**
     * 获取高级代理底下商户代付列表
     * @param sysWithdraw
     * @return
     */
    @Override
    public List<SysWithdraw> selectSysHighAgentWithdrawList(SysWithdraw sysWithdraw) {
        return sysWithdrawMapper.selectSysHighAgentWithdrawList(sysWithdraw);
    }

    /**
     * 获取高级代理底下商户代付统计
     * @param sysWithdraw
     * @return
     */
    @Override
    public WithdrawStatisticsRes selectSysHighAgentWithdrawStatistics(SysWithdraw sysWithdraw) {
        return sysWithdrawMapper.selectSysHighAgentWithdrawStatistics(sysWithdraw);
    }

    /**
     * 查询单位时间内高级代理底下商户成功代付金额
     * @param userCode
     * @param beginTime
     * @return
     */
    @Override
    public BigDecimal selectHighAgentSysWithdrawAmount(Integer userCode, Date beginTime, long parentId) {
        return sysWithdrawMapper.selectHighAgentSysWithdrawAmount(userCode,beginTime,parentId);
    }
}
